#include "crypt.h"
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <stdlib.h>
#include <memory.h>

#define BUFSIZE 4096

void usage() {
  printf("usage\n");
  printf("加密并签名:sign -es file --pubkey public.key --privkey private.key -o outfile\n");
  printf("解密并验签:sign -dv file --pubkey public.key --privkey private.key -o outfile\n");
}

int main(int argc, char **argv) {
  if(argc==9) {
    if((strcmp(argv[1],"-es")==0||strcmp(argv[1],"-dv")==0)&&strcmp(argv[3],"--pubkey")==0&&strcmp(argv[5],"--privkey")==0&&strcmp(argv[7],"-o")==0) {
      //打开文件
      int r_fd = open(argv[2], O_RDONLY);
      int w_fd = open(argv[8], O_RDWR | O_CREAT | O_TRUNC,00600);
      if(r_fd==-1||w_fd==-1) {
        printf("failed to open file\n");
        return 0;
      }
      //取出公钥和私钥
      char pub_key[2048],priv_key[2048];
      read_key_file(argv[4],pub_key);
      read_key_file(argv[6],priv_key);

      char read_buf[BUFSIZE+512]={0},write_buf[BUFSIZE+512]={0},hash[65],sign[257];
      char ivec[]=IVEC;
      //加密并签名
      if(strcmp(argv[1],"-es")==0) {
        //生成aes密钥
        char aes_key[AES_KEY_LEN*2];
        random_generate_aeskey(aes_key,AES_KEY_LEN);
        //使用rsa把aes_key加密，并放在out文件的前512字节处
        rsa_pub_encrypt(aes_key,AES_KEY_LEN,write_buf,pub_key);
        write(w_fd,write_buf,512);
        //计算文件的hash并使用私钥加密
        sha256_file(argv[2], hash);
        rsa_priv_encrypt(hash, 64, sign, priv_key);
        //读取明文文件，使用aes加密后写入密文文件中
        int len;
        while((len = read(r_fd,read_buf,BUFSIZE))>0) {
          if(len<BUFSIZE) {
            //已到达文件末尾，把签名接到后面一起加密
            memcpy(read_buf+len,sign, 256);
            len+=256;
            //使用PKCS7填充
          	size_t padd_num = 16 - len % 16;
          	for(int i=0;i<padd_num;i++)
              read_buf[len++]=padd_num;
          }
          len = aes_encrypt(read_buf,len,write_buf,aes_key,ivec);
          write(w_fd,write_buf,len);
        }
        printf("已生成文件%s\n",argv[8]);
      } else { //解密
        //先取出密文的前512字节,解密得到aes_key
        read(r_fd,read_buf,512);
        char aes_key[AES_KEY_LEN*2];
        rsa_priv_decrypt(read_buf,512,aes_key,priv_key);
        //通过aes_key解密密文
        int len,temp;//temp保存上次加密后write_buf中密文的长度，用于最后去填充
        while ((len=read(r_fd,read_buf,BUFSIZE))>0) {
          aes_decrypt(read_buf,len,write_buf,aes_key,ivec);
          write(w_fd,write_buf,len);
          temp=len;
        }
        //去掉aes加密的pkcs7填充
        ftruncate(w_fd,lseek(w_fd,0,SEEK_END)-(int)(write_buf[temp-1]));
        close(w_fd);

        //读取目标文件的最后256个字符并使用rsa公钥解密得到签名hash，同时删除文件末尾的签名
        w_fd = open(argv[8],O_RDWR);
        lseek(w_fd, -256, SEEK_END);
        read(w_fd,sign,256);
        len = lseek(w_fd,0,SEEK_END);
        ftruncate(w_fd,len-256);

        //计算目标文件hash
        sha256_file(argv[8],hash);

        //解密并比对签名
        rsa_pub_decrypt(sign,256,write_buf,pub_key);
        printf("file hash : %s\n",hash);
        printf("sign hash : %s\n", write_buf);
        if(strcmp(write_buf, hash)==0) {
          printf("已生成文件%s,通过签名认证\n",argv[8]);
        } else {
          printf("已生成文件%s,该文件签名未通过验证\n",argv[8]);
        }
      }
      close(r_fd);close(w_fd);
      return 0;
    }
  }
  usage();
  return 0;
}
